import axios, { AxiosRequestConfig } from "axios";
import { message } from "antd";
import { mapTo, share } from "rxjs/operators";
import { of, merge } from "rxjs";

const Ajv = require("ajv");
const ajv = new Ajv({ allErrors: true });

export class Api {
  private readonly _axios = axios.create({
    baseURL: "/api"
  });

  request<T>(config: AxiosRequestConfig) {
    return schema => {
      let data = config.data;
      if (config.method === "get") {
        data = config.params;
      }
      if (data) {
        isValid(schema.req, data);
      }
      return this._axios.request<T>(config).then(res => {
        isValid(schema.res, res.data);
        return res;
      });
    };
  }

  constructor() {
    this._axios.interceptors.request.use(
      config => {
        // Do something before request is sent
        // console.info('request', config);
        return config;
      },
      err => {
        // Do something with request error
        message.error(err.toString());
        return Promise.reject(err);
      }
    );

    this._axios.interceptors.response.use(
      res => {
        // Do something with response data
        // console.info('response', res);
        return res;
      },
      err => {
        // Do something with response error
        message.error(err.toString());
        return Promise.reject(err);
      }
    );
  }
}

/*export function Validator(reqSchema, resSchema): MethodDecorator {
    return ((target: object, propertyKey: PropertyKey, descriptor: PropertyDescriptor) => {
        const { value } = descriptor;
        descriptor.value = (req) => {
            isValid(reqSchema, req);
            return value(req).then(res => {
                isValid(resSchema, res);
                return res;
            });
        };
    });
}*/

function isValid(schema, data) {
  const is = ajv.validate(schema, data);
  if (!is) {
    console.warn("validator error", ajv.errors);
  }
}

export function wrapLoading(api$, key: string, Action) {
  api$ = api$.pipe(share());
  const begin$ = of({
    ...new Action({
      [key]: true
    })
  });
  const end$ = api$.pipe(
    mapTo({
      ...new Action({
        [key]: false
      })
    })
  );
  return merge(begin$, api$, end$);
}
